/*
-----------------------------------------------------------------------------
Filename:    BaseEngine.cpp
-----------------------------------------------------------------------------
*/
#include "BaseEngine.h"
#include "../res/resource.h"



//-------------------------------------------------------------------------------------
BaseEngine::BaseEngine(void)
    : mRoot(0),
    mCamera(0),
    mSceneMgr(0),
    mWindow(0),
    mResourcesCfg(Ogre::StringUtil::BLANK),
    mPluginsCfg(Ogre::StringUtil::BLANK),
    mTrayMgr(0),
    mCameraMan(0),
    mDetailsPanel(0),
    mCursorWasVisible(false),
    mShutDown(false),
    mInputManager(0),
    mMouse(0),
    mKeyboard(0)
{
	nLastTick0 = GetTickCount();
	nLastTick1 = GetTickCount();
	nLastTick2 = GetTickCount();
	nCurrentTime = 0;
	nCurrentTimeScale1 = 0;
	nCurrentTimeScale2 = 0;
	mCameraAnimState = 0;

	mPredefinedCameraDescriptions = new PredefinedCameraDescriptions();
}

//-------------------------------------------------------------------------------------
BaseEngine::~BaseEngine(void)
{
    if (mTrayMgr) delete mTrayMgr;
    if (mCameraMan) delete mCameraMan;

    //Remove ourself as a Window listener
    Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
    windowClosed(mWindow);
    delete mRoot;
}

//-------------------------------------------------------------------------------------
bool BaseEngine::configure(void)
{
   
	mRoot->restoreConfig();
  
        mWindow = mRoot->initialise(true, "SceneView Render Window");

       
        HWND hwnd;
        mWindow->getCustomAttribute("WINDOW", (void*)&hwnd);
        LONG iconID   = (LONG)LoadIcon( GetModuleHandle(0), MAKEINTRESOURCE(IDI_APPICON) );
        SetClassLong( hwnd, GCL_HICON, iconID );
        return true;
  
}
//-------------------------------------------------------------------------------------
void BaseEngine::chooseSceneManager(void)
{
    
    mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
}
//-------------------------------------------------------------------------------------
void BaseEngine::createCamera(void)
{
    // Create the camera
    mCamera = mSceneMgr->createCamera("PlayerCam");

   
    mCamera->setPosition(Ogre::Vector3(0,0,80));
   
    mCamera->lookAt(Ogre::Vector3(0,0,-300));
    mCamera->setNearClipDistance(5);

    mCameraMan = new OgreBites::SdkCameraMan(mCamera);   // create a default camera controller
	camNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
}

//-------------------------------------------------------------------------------------

void BaseEngine::giveCameraControlToPlayer(void)
{
	camNode->detachAllObjects();
	mCameraMan->setStyle(OgreBites::CameraStyle::CS_FREELOOK); 
    mCamera->setAutoTracking(false); 
	if(mCameraAnimState!=0)
	{
		mCameraAnimState->setEnabled(false);
	}
}

//-------------------------------------------------------------------------------------

void BaseEngine::cameraFollowPath(Ogre::SceneNode *followNode, std::vector<Ogre::Vector3> path)
{
		mCameraMan->setStyle(OgreBites::CameraStyle::CS_MANUAL); 
        mCamera->setAutoTracking(true, followNode); 


        camNode->attachObject(mCamera);

		if(mCameraAnimState!=0)
		{
		mSceneMgr->destroyAnimation("CameraTrack");
		mSceneMgr->destroyAnimationState("CameraTrack");
		}

        Ogre::Animation* anim = mSceneMgr->createAnimation("CameraTrack", 10);
        anim->setInterpolationMode(Ogre::Animation::IM_SPLINE);


        Ogre::NodeAnimationTrack* track = anim->createNodeTrack(0, camNode);

		Ogre::Real index = 0;

		for(int i=0;i<path.size();i++)
		{
			track->createNodeKeyFrame(index)->setTranslate(path.at(i));
			index = index + 2.5;
		}

        // create a new animation state to track this
        mCameraAnimState = mSceneMgr->createAnimationState("CameraTrack");
        mCameraAnimState->setEnabled(true);
}

//-------------------------------------------------------------------------------------

void BaseEngine::createFrameListener(void)
{
    Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
    OIS::ParamList pl;
    size_t windowHnd = 0;
    std::ostringstream windowHndStr;

    mWindow->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

    mInputManager = OIS::InputManager::createInputSystem( pl );

    mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
    mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));

    mMouse->setEventCallback(this);
    mKeyboard->setEventCallback(this);

    //Set initial mouse clipping size
    windowResized(mWindow);

    //Register as a Window listener
    Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);

    mTrayMgr = new OgreBites::SdkTrayManager("InterfaceName", mWindow, mMouse, this);
    mTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
    mTrayMgr->hideCursor();

    // create a params panel for displaying additional debug info
    Ogre::StringVector items;
    items.push_back("cam.pX");
    items.push_back("cam.pY");
    items.push_back("cam.pZ");
    items.push_back("");
    items.push_back("cam.oW");
    items.push_back("cam.oX");
    items.push_back("cam.oY");
    items.push_back("cam.oZ");
    items.push_back("");
    items.push_back("Filtering");
    items.push_back("Poly Mode");

    mDetailsPanel = mTrayMgr->createParamsPanel(OgreBites::TL_NONE, "DetailsPanel", 200, items);
    mDetailsPanel->setParamValue(9, "Bilinear");
    mDetailsPanel->setParamValue(10, "Solid");
    mDetailsPanel->hide();

    mRoot->addFrameListener(this);
}
//-------------------------------------------------------------------------------------
void BaseEngine::destroyScene(void)
{
}
//-------------------------------------------------------------------------------------
void BaseEngine::stopPhysics()
{
	NxOgre::World::destroyWorld();
}
//-------------------------------------------------------------------------------------
void BaseEngine::createViewports(void)
{
    // Create one viewport, entire window
    Ogre::Viewport* vp = mWindow->addViewport(mCamera);
    vp->setBackgroundColour(Ogre::ColourValue(0,0,0));

    // Alter the camera aspect ratio to match the viewport
    mCamera->setAspectRatio(
        Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
}
//-------------------------------------------------------------------------------------
void BaseEngine::setupResources(void)
{
    // Load resource paths from config file
    Ogre::ConfigFile cf;
    cf.load(mResourcesCfg);

    // Go through all sections & settings in the file
    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

    Ogre::String secName, typeName, archName;
    while (seci.hasMoreElements())
    {
        secName = seci.peekNextKey();
        Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
        Ogre::ConfigFile::SettingsMultiMap::iterator i;
        for (i = settings->begin(); i != settings->end(); ++i)
        {
            typeName = i->first;
            archName = i->second;
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
                archName, typeName, secName);
        }
    }
}
//-------------------------------------------------------------------------------------
void BaseEngine::createResourceListener(void)
{

}
//-------------------------------------------------------------------------------------
void BaseEngine::loadResources(void)
{
    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}
//-------------------------------------------------------------------------------------
void BaseEngine::go(void)
{
#ifdef _DEBUG
    mResourcesCfg = "resources_d.cfg";
    mPluginsCfg = "plugins_d.cfg";
#else
    mResourcesCfg = "resources.cfg";
    mPluginsCfg = "plugins.cfg";
#endif

    if (!setup())
        return;

    mRoot->startRendering();

    // clean up
    destroyScene();
}
//-------------------------------------------------------------------------------------
bool BaseEngine::setup(void)
{
    mRoot = new Ogre::Root(mPluginsCfg);

    setupResources();

    bool carryOn = configure();
    if (!carryOn) return false;

    chooseSceneManager();
    createCamera();
    createViewports();

    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);


    createResourceListener();

    loadResources();

    createFrameListener();


    createScene();

    return true;
};
//-------------------------------------------------------------------------------------
bool BaseEngine::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
    if(mWindow->isClosed())
        return false;

    if(mShutDown)
        return false;


    mKeyboard->capture();
    mMouse->capture();

    mTrayMgr->frameRenderingQueued(evt);

    if (!mTrayMgr->isDialogVisible())
    {
        mCameraMan->frameRenderingQueued(evt);   
        if (mDetailsPanel->isVisible())   
        {
            mDetailsPanel->setParamValue(0, Ogre::StringConverter::toString(mCamera->getDerivedPosition().x));
            mDetailsPanel->setParamValue(1, Ogre::StringConverter::toString(mCamera->getDerivedPosition().y));
            mDetailsPanel->setParamValue(2, Ogre::StringConverter::toString(mCamera->getDerivedPosition().z));
            mDetailsPanel->setParamValue(4, Ogre::StringConverter::toString(mCamera->getDerivedOrientation().w));
            mDetailsPanel->setParamValue(5, Ogre::StringConverter::toString(mCamera->getDerivedOrientation().x));
            mDetailsPanel->setParamValue(6, Ogre::StringConverter::toString(mCamera->getDerivedOrientation().y));
            mDetailsPanel->setParamValue(7, Ogre::StringConverter::toString(mCamera->getDerivedOrientation().z));
        }
    }

	DWORD tick = GetTickCount();
	if(tick - nLastTick2 > 10000)
	{
		nCurrentTimeScale2++;
		nLastTick2 = tick;
	}
	else if(tick - nLastTick1 > 1000)
	{
		nCurrentTimeScale1++;
		nLastTick1 = tick;
	}
	else if(tick - nLastTick0 > 100)
	{
		nCurrentTime++;
		nLastTick0 = tick;
	}

	if(mCameraAnimState!=0)
	{
		mCameraAnimState->addTime(evt.timeSinceLastFrame);
	}

    return true;
}
//-------------------------------------------------------------------------------------
bool BaseEngine::keyPressed( const OIS::KeyEvent &arg )
{


    if (mTrayMgr->isDialogVisible()) return true;  

    if (arg.key == OIS::KC_F)   
    {
        mTrayMgr->toggleAdvancedFrameStats();
    }
    else if (arg.key == OIS::KC_G)   
    {
        if (mDetailsPanel->getTrayLocation() == OgreBites::TL_NONE)
        {
            mTrayMgr->moveWidgetToTray(mDetailsPanel, OgreBites::TL_TOPRIGHT, 0);
            mDetailsPanel->show();
        }
        else
        {
            mTrayMgr->removeWidgetFromTray(mDetailsPanel);
            mDetailsPanel->hide();
        }
    }
    else if (arg.key == OIS::KC_T)   // cycle polygon rendering mode
    {
        Ogre::String newVal;
        Ogre::TextureFilterOptions tfo;
        unsigned int aniso;

        switch (mDetailsPanel->getParamValue(9).asUTF8()[0])
        {
        case 'B':
            newVal = "Trilinear";
            tfo = Ogre::TFO_TRILINEAR;
            aniso = 1;
            break;
        case 'T':
            newVal = "Anisotropic";
            tfo = Ogre::TFO_ANISOTROPIC;
            aniso = 8;
            break;
        case 'A':
            newVal = "None";
            tfo = Ogre::TFO_NONE;
            aniso = 1;
            break;
        default:
            newVal = "Bilinear";
            tfo = Ogre::TFO_BILINEAR;
            aniso = 1;
        }

        Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
        Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(aniso);
        mDetailsPanel->setParamValue(9, newVal);
    }
    else if (arg.key == OIS::KC_R)   // cycle polygon rendering mode
    {
        Ogre::String newVal;
        Ogre::PolygonMode pm;

        switch (mCamera->getPolygonMode())
        {
        case Ogre::PM_SOLID:
            newVal = "Wireframe";
            pm = Ogre::PM_WIREFRAME;
            break;
        case Ogre::PM_WIREFRAME:
            newVal = "Points";
            pm = Ogre::PM_POINTS;
            break;
        default:
            newVal = "Solid";
            pm = Ogre::PM_SOLID;
        }

        mCamera->setPolygonMode(pm);
        mDetailsPanel->setParamValue(10, newVal);
    }
    else if(arg.key == OIS::KC_F5)   
    {
        Ogre::TextureManager::getSingleton().reloadAll();
    }
    else if (arg.key == OIS::KC_SYSRQ)   // take a screenshot
    {
        mWindow->writeContentsToTimestampedFile("screenshot", ".jpg");
    }
    else if (arg.key == OIS::KC_ESCAPE)
    {
        mShutDown = true;
    }


    mCameraMan->injectKeyDown(arg);
    return true;
}

bool BaseEngine::keyReleased( const OIS::KeyEvent &arg )
{
    mCameraMan->injectKeyUp(arg);
    return true;
}

bool BaseEngine::mouseMoved( const OIS::MouseEvent &arg )
{
    if (mTrayMgr->injectMouseMove(arg)) return true;
    mCameraMan->injectMouseMove(arg);
    return true;
}

bool BaseEngine::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
    if (mTrayMgr->injectMouseDown(arg, id)) return true;
    mCameraMan->injectMouseDown(arg, id);
    return true;
}

bool BaseEngine::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
    if (mTrayMgr->injectMouseUp(arg, id)) return true;
    mCameraMan->injectMouseUp(arg, id);
    return true;
}

//Adjust mouse clipping area
void BaseEngine::windowResized(Ogre::RenderWindow* rw)
{
    unsigned int width, height, depth;
    int left, top;
    rw->getMetrics(width, height, depth, left, top);

    const OIS::MouseState &ms = mMouse->getMouseState();
    ms.width = width;
    ms.height = height;
}

//Unattach OIS before window shutdown
void BaseEngine::windowClosed(Ogre::RenderWindow* rw)
{
    
    if( rw == mWindow )
    {
        if( mInputManager )
        {
            mInputManager->destroyInputObject( mMouse );
            mInputManager->destroyInputObject( mKeyboard );

            OIS::InputManager::destroyInputSystem(mInputManager);
            mInputManager = 0;
        }
    }
}


bool BaseEngine::fileExists(Ogre::String fileName)
{
  struct stat stFileInfo; 
  bool blnReturn; 
  int intStat; 

  intStat = stat(fileName.c_str(),&stFileInfo); 
  if(intStat == 0) { 
    blnReturn = true; 
  } else { 
    blnReturn = false; 
  } 
   
  return(blnReturn); 
}


DWORD BaseEngine::getCurrentTimeScale0(void)
{
	return nCurrentTime;
}


DWORD BaseEngine::getCurrentTimeScale1(void)
{
	return nCurrentTimeScale1;
}


DWORD BaseEngine::getCurrentTimeScale2(void)
{
	return nCurrentTimeScale2;
}


Ogre::String BaseEngine::getBenchMarkData(void)
{
	float worstFPS = mWindow->getWorstFPS();
	float bestFPS = mWindow->getBestFPS();
	float averageFPS = mWindow->getAverageFPS();

	Ogre::String s_worstFPS = ", Worst FPS: ";
	s_worstFPS.append(Ogre::StringConverter::toString(worstFPS));

	Ogre::String s_bestFPS = ", Best FPS: ";
	s_bestFPS.append(Ogre::StringConverter::toString(bestFPS));

	Ogre::String s_averageFPS = "Average FPS: ";
	s_averageFPS.append(Ogre::StringConverter::toString(averageFPS));

	Ogre::String benchmark = "";
	benchmark.append(s_averageFPS);
	benchmark.append(s_bestFPS);
	benchmark.append(s_worstFPS);

	return benchmark;
}

void BaseEngine::resetBenchMarkData(void)
{
	mWindow->resetStatistics();
}

bool BaseEngine::connectToServer(Ogre::String hostAddress, int port)
{
	return mNetwork.connect(hostAddress,port);
}


bool BaseEngine::sendMessageToServer(Ogre::String message)
{
	return mNetwork.sendString(message);
}


Ogre::String BaseEngine::receiveMessageFromServer(void)
{
	return mNetwork.receiveString();
}


